﻿using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using JQuery.WebKit.Common.Attributes;
using JQuery.WebKit.Common.Utilities;

namespace JQuery.WebKit.Common.Extenstions
{
    public static class TypeExtensions
    {
        public static PropertyInfo[] GetFilteredProperties(this Type type)
        {
            return type.GetProperties().Where(pi => !Attribute.IsDefined(pi, typeof(SkipPropertyAttribute))).ToArray();
        }

        public static object GetPropertyValue(this object componentInstance, string propertyName)
        {
            if (componentInstance == null)
            {
                return null;
            }

            return Reflection.GetValue(componentInstance, propertyName, true, true);
        }


        public static PropertyInfo[] GetFilteredProperties(this Type type, BindingFlags bindingAttr)
        {
            return type.GetProperties(bindingAttr).Where(pi => !Attribute.IsDefined(pi, typeof(SkipPropertyAttribute))).ToArray();
        }

        public static T Parse<T>(this object value, out bool success)
        {
            try
            {
                Type type = typeof(T);
                success = true;

                if (type.Name == "Nullable`1")
                    type = Nullable.GetUnderlyingType(type);

                if (type.BaseType.Name == "Enum" || (type.Name == "Enum"))
                    return (T)Enum.Parse(type, value.ToString().Replace(" ", ""));

                if (type.Name == "Guid")
                    return (T)(object)(new Guid(value.ToString()));

                return (T)Convert.ChangeType(value, type);
            }
            catch (Exception) 
            {
                success = false;
                return default(T); 
            }
        }

        public static bool TryParse(this object value, Type outputType, out object outputValue)
        {
            outputValue = null;
            try
            {
                Type type = outputType;

                if (type.Name == "Nullable`1")
                    type = Nullable.GetUnderlyingType(type);

                if (type.BaseType.Name == "Enum" || (type.Name == "Enum"))
                    outputValue = Enum.Parse(type, value.ToString().Replace(" ", ""));

                if (type.Name == "Guid")
                    outputValue = (object)(new Guid(value.ToString()));

                outputValue = Convert.ChangeType(value, type);
                return true;
            }
            catch (Exception)
            {
                outputValue = outputType;
                return false;
            }
        }

        public static Attribute GetAttribute(this object componentInstance, string propertyName, Type attributeType)
        {
            PropertyInfo propertyInfo = componentInstance.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
            if (propertyInfo == null)
            {
                return null;
            }
            return componentInstance.GetAttribute(propertyInfo, attributeType);
        }

        public static Attribute GetAttribute(this object componentInstance, PropertyInfo propertyInfo, Type attributeType)
        {
            if (attributeType == null || propertyInfo == null)
            {
                return null;
            }
            System.ComponentModel.AttributeCollection attributes =
                TypeDescriptor.GetProperties(propertyInfo.DeclaringType)[propertyInfo.Name].Attributes;

            return attributes[attributeType];
        }

        public static bool IsDefaultValue(this object componentInstance, string propertyName)
        {
            if (componentInstance == null)
            {
                return false;
            }

            DefaultValueAttribute defVal = (DefaultValueAttribute)GetAttribute(componentInstance, propertyName, typeof(DefaultValueAttribute));
            if (defVal == null)
            {
                return false;
            }
            object propVal = GetPropertyValue(componentInstance, propertyName);
            return defVal.Value.Equals(propVal);
        }
    }
}
